// Copyright (c) 2023 Presto Labs Pte. Ltd.
// Author: qiang

#include "coin2/exchange/mercado/api_order/native_rest_async_asio_subscriber.h"

const int64_t day_ns = 24 * 60 * 60 * 1'000'000'000LL;
namespace coin2::exchange::mercado::api_order {

void MercadoRestAsyncAsioSubscriber::QueryAccountBalance() {
  RestParams params;

  std::shared_ptr<AsioHttpContext> http_context;
  if (!rest_client()->HasAccountId()) {
    return;
  } else {
    http_context = rest_client()->QueryAccountBalance();
  }

  if (!http_context) {
    return;
  }
  http_context->handler =
      std::bind(&MercadoRestAsyncAsioSubscriber::HandleQuery, this, std::placeholders::_1);
  http_driver_->PostAsyncContext(http_context);
}

// Enum status : pending, working, cancelled, filled
void MercadoRestAsyncAsioSubscriber::QueryOrderList(
    const std::string& native_symbol,
    const std::string status,
    const std::string side) {
  if (rest_client()->HasAccountId()) {
    RestParams params;
    if (status.size() > 0) {
      params.set_status(status);
    }
    if (side.size() > 0) {
      params.set_side(side);
    }

    params.set_symbol(native_symbol);
    auto http_context = rest_client()->ListOrder(params);
    if (!http_context) return;
    http_context->handler =
        std::bind(&MercadoRestAsyncAsioSubscriber::HandleQuery, this, std::placeholders::_1);
    http_driver_->PostAsyncContext(http_context);
    return;
  }

  return;
}

bool MercadoRestAsyncAsioSubscriber::TokenIsExpiration() {
  int64_t expiration = rest_client()->GetTokenExpiration();
  int64_t cur_time = GetCurrentTimestamp();
  if (expiration - cur_time < day_ns) {
    return true;
  }

  return false;
}

void MercadoRestAsyncAsioSubscriber::QueryToken() {
  std::shared_ptr<AsioHttpContext> http_context;
  if (TokenIsExpiration()) {
    http_context = rest_client()->QueryToken();
  } else if (!rest_client()->HasAccountId()) {
    http_context = rest_client()->Accounts();
  }

  if (!http_context) return;
  http_context->handler =
      std::bind(&MercadoRestAsyncAsioSubscriber::HandleQuery, this, std::placeholders::_1);
  http_driver_->PostAsyncContext(http_context);
}

void MercadoRestAsyncAsioSubscriber::SubscribeAsync() {
  AddPeriodicTask(std::bind(&MercadoRestAsyncAsioSubscriber::QueryToken, this), 1, "query_token");

  AddPeriodicTask(
      std::bind(&MercadoRestAsyncAsioSubscriber::QueryAccountBalance, this),
      config_.query_account_balance_period_sec(),
      "query_balance");

  const auto symbol_list = native_symbols();

  AddPeriodicTask(
      std::bind(
          &MercadoRestAsyncAsioSubscriber::QueryOrderList,
          this,
          std::placeholders::_1,
          "working",
          "buy"),
      config_.query_open_orders_period_sec(),
      "query_open_orders_buy",
      symbol_list);

  AddPeriodicTask(
      std::bind(
          &MercadoRestAsyncAsioSubscriber::QueryOrderList,
          this,
          std::placeholders::_1,
          "working",
          "sell"),
      config_.query_open_orders_period_sec(),
      "query_open_orders_sell",
      symbol_list);

  AddPeriodicTask(
      std::bind(
          &MercadoRestAsyncAsioSubscriber::QueryOrderList,
          this,
          std::placeholders::_1,
          "cancelled",
          ""),
      config_.query_cancel_orders_period_sec(),
      "query_open_orders",
      symbol_list);

  AddPeriodicTask(
      std::bind(
          &MercadoRestAsyncAsioSubscriber::QueryOrderList,
          this,
          std::placeholders::_1,
          "filled",
          ""),
      config_.query_fill_period_sec(),
      "query_filled_orders",
      symbol_list);
}

}  // namespace coin2::exchange::mercado::api_order
